package com.keith.jpa.tenant.utils;

import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.keith.jpa.tenant.TenantJpaInspector;
import com.keith.jpa.tenant.config.TenantJpaConfig;
import com.keith.jpa.tenant.config.TenantPenetration;
import com.keith.jpa.tenant.context.TenantContextHolder;
import com.keith.jpa.tenant.context.UserTenant;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.schema.Column;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.jpa.repository.query.QueryEnhancerFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;

import javax.persistence.Table;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * TenantUtils.java
 * 租户工具类
 *
 * @author czykeih
 * @version 1.0 2023/10/25
 */
@Slf4j
public class TenantUtils {
    private static final Map<String, String> entityTableMaps = Maps.newConcurrentMap();

    /**
     * 获取配置对象
     *
     * @return
     */
    public static TenantJpaConfig jpaConfig() {
        return TenantContextHolder.getBean(TenantJpaConfig.class);
    }

    public static ExpressionList getTenantIds(String tenantId) {
        TenantPenetration bean = TenantContextHolder.getBean(TenantPenetration.class);
        List<Object> tenantIds = Lists.newArrayList(tenantId);
        if (bean != null) {
            tenantIds = bean.getTenantIds(tenantId);
        }
        ExpressionList expressions = new ExpressionList();
        if ("Long".equalsIgnoreCase(jpaConfig().getTenantColumnType())) {
            tenantIds.forEach(v -> expressions.addExpressions(new LongValue(Long.parseLong(String.valueOf(v)))));
        } else {
            tenantIds.forEach(v -> expressions.addExpressions(new StringValue(String.valueOf(v))));
        }
        return expressions;
    }

    /**
     * 验证当前是否需要租户处理
     *
     * @return
     */
    public static boolean verifyTenant() {
        UserTenant tenant = TenantContextHolder.getTenant();
        if (tenant == null) {
            return jpaConfig().isDefaultTenant();
        }
        return tenant.getTenant();
    }

    public static boolean verifyPenetration() {
        UserTenant tenant = TenantContextHolder.getTenant();
        if (tenant == null) {
            return jpaConfig().isPenetration();
        }
        return tenant.getPenetration();
    }

    public static boolean checkCurrentTenantStatus() {
        return jpaConfig().isEnableTenant() && verifyTenant() && !Strings.isNullOrEmpty(TenantContextHolder.getTenantId());
    }

    /**
     * 校验是否忽略表
     *
     * @param tableName
     * @return
     */
    public static boolean verifyTableName(String tableName) {
        boolean status = jpaConfig().getIgnoreTablesSet().stream().anyMatch(v -> v.equalsIgnoreCase(tableName));
        if (jpaConfig().isLog() && status) {
            log.info("忽略表:[{}]的租户处理", tableName);
        }
        return !status;
    }

    /**
     * 校验是否已经包含租户信息
     *
     * @param columns
     * @return
     */
    public static int verifyColumnName(List<Column> columns) {
        if (!CollectionUtils.isEmpty(columns)) {
            for (int i = 0; i < columns.size(); i++) {
                if (jpaConfig().getTenantColumn().equalsIgnoreCase(columns.get(i).getFullyQualifiedName())) {
                    return i;
                }
            }
        }
        return -1;
    }

    /**
     * 建造租户条件
     *
     * @return
     */
    public static Expression getTenantExpression() {
        TenantJpaConfig tenantJpaConfig = TenantUtils.jpaConfig();
        if ("Long".equals(tenantJpaConfig.getTenantColumnType())) {
            return new LongValue(Objects.requireNonNull(TenantContextHolder.getTenantId()));
        }
        return new StringValue(Objects.requireNonNull(TenantContextHolder.getTenantId()));
    }

    /**
     * 验证租户ID是否要忽略掉，统一处理
     *
     * @param value
     * @return
     */
    public static boolean verifyTenantIdValue(String value) {
        boolean status = jpaConfig().getFilterTenantIds().stream().anyMatch(v -> v.equalsIgnoreCase(value));
        if (jpaConfig().isLog() && status) {
            log.info("忽略租户ID值:[{}],替换为:[{}]", value, TenantContextHolder.getTenantId());
        }
        return status;
    }

    /**
     * 从po类获取对应数据库表
     *
     * @param entity
     * @return
     */
    public static String getTableName(Object entity) {
        String tableName = entityTableMaps.get(entity.getClass().getName());
        if (Strings.isNullOrEmpty(tableName)) {
            Table table = AnnotationUtils.findAnnotation(entity.getClass(), Table.class);
            if (table != null) {
                tableName = table.name();
            }
        }
        if (!Strings.isNullOrEmpty(tableName)) {
            entityTableMaps.put(entity.getClass().getName(), tableName);
        }
        return tableName;
    }

    /**
     * 转换租户值
     *
     * @param entity
     */
    public static void transTenantId(Object entity, Object[] state, String[] propertyNames) {
        try {
            Field field = null;
            int index = -1;
            if ((index = findStateIndex(propertyNames)) >= 0) {
                if ("Long".equalsIgnoreCase(jpaConfig().getTenantColumnType())) {
                    field = ReflectionUtils.findField(entity.getClass(), jpaConfig().getTenantIdFieldName(), Long.class);
                } else {
                    field = ReflectionUtils.findField(entity.getClass(), jpaConfig().getTenantIdFieldName(), String.class);
                }
                if (field != null) {
                    field.setAccessible(true);
                    Object value = field.get(entity);
                    if (value == null || Strings.isNullOrEmpty(String.valueOf(value)) || TenantUtils.verifyTenantIdValue(String.valueOf(value))) {
                        if ("Long".equalsIgnoreCase(jpaConfig().getTenantColumnType())) {
                            field.set(entity, Long.valueOf(TenantContextHolder.getTenantId()));
                            state[index] = Long.valueOf(TenantContextHolder.getTenantId());
                        } else {
                            field.set(entity, TenantContextHolder.getTenantId());
                            state[index] = TenantContextHolder.getTenantId();
                        }
                        if (TenantUtils.jpaConfig().isLog()) {
                            log.info("租户ID[{}]替换为:[{}]", value, TenantContextHolder.getTenantId());
                        }
                    }
                }
            }
        } catch (Exception e) {
            log.error("替换tenantId租户字段异常", e);
        }
    }

    private static int findStateIndex(String[] propertyNames) {
        for (int i = 0; i < propertyNames.length; i++) {
            if (propertyNames[i].equals(jpaConfig().getTenantIdFieldName())) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 解决版本升级导致的spring-boot 启动报错问题
     */
    public static void disableStartJpaParser() {
        try {
            QueryEnhancerFactory.forQuery(null);
        } catch (Exception e) {
            // 不需要处理
        }
        try {
            Field sparser = QueryEnhancerFactory.class.getDeclaredField("JSQLPARSER_IN_CLASSPATH");
            Field modifiers = Field.class.getDeclaredField("modifiers");
            sparser.setAccessible(true);
            modifiers.setAccessible(true);
            modifiers.setInt(sparser,
                    sparser.getModifiers() & ~Modifier.FINAL);
            sparser.setBoolean(null, false);
        } catch (Exception e) {
            // 不需要处理
        }
    }
    public static String tenantSql(String sql) {
        return new TenantJpaInspector().inspect(sql);
    }
}
